以取得個股盤後資訊為例。
https://www.twse.com.tw/exchangeReport/MI_INDEX?response=json&type=ALLBUT0999&date=20190904

class AfterHoursInfo:
    def __init__(
        self,
        code,
        name,
        totalShare,
        totalTurnover,
        openPrice,
        highestPrice,
        lowestPrice,
        closePrice):
        # 代碼
        self.Code = code
        # 名稱
        self.Name = name
        # 成交股數
        self.TotalShare = totalShare
        # 成交金額
        self.TotalTurnover = totalTurnover
        # 開盤價
        self.OpenPrice = openPrice
        # 最高價
        self.HighestPrice = highestPrice
        # 最低價
        self.LowestPrice = lowestPrice
        # 收盤價
        self.ClosePrice = closePrice
import datetime
import json
import os
import re
import loguru
import requests
def main():
    # 下載當日個股盤後資訊
    resp = requests.get(
        f'https://www.twse.com.tw/exchangeReport/MI_INDEX?' +
        f'response=json&' +
        f'type=ALLBUT0999' +
        f'&date={datetime.date.today():%Y%m%d}'
    )
    if resp.status_code != 200:
        loguru.logger.error('RESP: status code is not 200')
    loguru.logger.success('RESP: success')
    # 盤後資訊清單
    afterHoursInfos = []
    # 取出 JSON 內容
    body = resp.json()
    # 取出 stat 欄位
    stat = body['stat']
    # 如果 stat 不是 OK,代表查詢日期尚無資料
    if stat != 'OK':
        loguru.logger.error(f'RESP: body.stat error is {stat}.')
        return
    # 取出第 9 表格內容
    records = body['data9']
    # 依序取出每筆盤後資訊
    for record in records:
        # 取出代碼欄位值
        code = record[0].strip()
        # 符合股票代碼規則才處理
        if re.match(r'^[1-9][0-9][0-9][0-9]$', code) is not None:
            # 取出名稱欄位值
            name = record[1].strip()
            # 取出成交股數欄位值
            totalShare = record[2].replace(',', '').strip()
            # 取出成交金額欄位值
            totalTurnover = record[4].replace(',', '').strip()
            # 取出開盤價欄位值
            openPrice = record[5].replace(',', '').strip()
            # 取出最高價欄位值
            highestPrice = record[6].replace(',', '').strip()
            # 取出最低價欄位值
            lowestPrice = record[7].replace(',', '').strip()
            # 取出收盤價欄位值
            closePrice = record[8].replace(',', '').strip()
            afterHoursInfo = AfterHoursInfo(
                code=code,
                name=name,
                totalShare=totalShare,
                totalTurnover=totalTurnover,
                openPrice=openPrice,
                highestPrice=highestPrice,
                lowestPrice=lowestPrice,
                closePrice=closePrice
            )
            afterHoursInfos.append(afterHoursInfo)
    # 將每筆物件表達式輸出的字串以系統換行符號相接,讓每筆物件表達式各自獨立一行
    message = os.linesep.join([
        str(afterHoursInfo)
        for afterHoursInfo in afterHoursInfos
    ])
    loguru.logger.info('AFTERHOURSINFOS' + os.linesep + message)
if __name__ == '__main__':
    loguru.logger.add(
        f'{datetime.date.today():%Y%m%d}.log',
        rotation='1 day',
        retention='7 days',
        level='DEBUG'
    )
    main()
因為存入資料都是字串,但實際內容有整數(成交股數、成交金額),還有浮點數(開盤價、最高價、最低價、收盤價),所以需要在儲存類別的建構子中進行轉換處理。
import fractions
class AfterHoursInfo:
    def __init__(
        self,
        code,
        name,
        totalShare,
        totalTurnover,
        openPrice,
        highestPrice,
        lowestPrice,
        closePrice):
        # 代碼
        self.Code = code
        # 名稱
        self.Name = name
        # 成交股數
        self.TotalShare = int(totalShare)
        # 成交金額
        self.TotalTurnover = int(totalTurnover)
        # 開盤價
        self.OpenPrice = fractions.Fraction(openPrice)
        # 最高價
        self.HighestPrice = fractions.Fraction(highestPrice)
        # 最低價
        self.LowestPrice = fractions.Fraction(lowestPrice)
        # 收盤價
        self.ClosePrice = fractions.Fraction(closePrice)
    # 物件表達式
    def __repr__(self):
        totalShare = self.TotalShare
        if totalShare is not None:
            totalShare = f'{totalShare:.2f}'
        totalTurnover = self.TotalTurnover
        if totalTurnover is not None:
            totalTurnover = f'{totalTurnover:.2f}'
        openPrice = self.OpenPrice
        if openPrice is not None:
            openPrice = f'{openPrice:.2f}'
        highestPrice = self.HighestPrice
        if highestPrice is not None:
            highestPrice = f'{highestPrice:.2f}'
        lowestPrice = self.LowestPrice
        if lowestPrice is not None:
            lowestPrice = f'{lowestPrice:.2f}'
        closePrice = self.ClosePrice
        if closePrice is not None:
            closePrice = f'{closePrice:.2f}'
        return (
            f'class AfterHoursInfo {{ '
            f'Code={self.Code}, '
            f'Name={self.Name}, '
            f'TotalShare={totalShare}, '
            f'TotalTurnover={totalTurnover}, '
            f'OpenPrice={openPrice}, '
            f'HighestPrice={highestPrice}, '
            f'LowestPrice={float(lowestPrice):.2f}, '
            f'ClosePrice={closePrice} '
            f'}}'
        )
但執行後發現與前一日相同數值者會標示為 --,造成轉型錯誤,所以重新修正。
import datetime
import fractions
import json
import os
import re
import loguru
import requests
class AfterHoursInfo:
    def __init__(
        self,
        code,
        name,
        totalShare,
        totalTurnover,
        openPrice,
        highestPrice,
        lowestPrice,
        closePrice):
        # 代碼
        self.Code = code
        # 名稱
        self.Name = name
        # 成交股數
        self.TotalShare = self.checkNumber(totalShare)
        if self.TotalShare is not None:
            self.TotalShare = int(totalShare)
        # 成交金額
        self.TotalTurnover = self.checkNumber(totalTurnover)
        if self.TotalTurnover is not None:
            self.TotalTurnover = int(totalTurnover)
        # 開盤價
        self.OpenPrice = self.checkNumber(openPrice)
        if self.OpenPrice is not None:
            self.OpenPrice = fractions.Fraction(openPrice)
        # 最高價
        self.HighestPrice = self.checkNumber(highestPrice)
        if self.HighestPrice is not None:
            self.HighestPrice = fractions.Fraction(highestPrice)
        # 最低價
        self.LowestPrice = self.checkNumber(lowestPrice)
        if self.LowestPrice is not None:
            self.LowestPrice = fractions.Fraction(lowestPrice)
        # 收盤價
        self.ClosePrice = self.checkNumber(closePrice)
        if self.ClosePrice is not None:
            self.ClosePrice = fractions.Fraction(closePrice)
    # 物件表達式
    def __repr__(self):
        totalShare = self.TotalShare
        if totalShare is not None:
            totalShare = f'{totalShare}'
        totalTurnover = self.TotalTurnover
        if totalTurnover is not None:
            totalTurnover = f'{totalTurnover}'
        openPrice = self.OpenPrice
        if openPrice is not None:
            openPrice = f'{float(openPrice):.2f}'
        highestPrice = self.HighestPrice
        if highestPrice is not None:
            highestPrice = f'{float(highestPrice):.2f}'
        lowestPrice = self.LowestPrice
        if lowestPrice is not None:
            lowestPrice = f'{float(lowestPrice):.2f}'
        closePrice = self.ClosePrice
        if closePrice is not None:
            closePrice = f'{float(closePrice):.2f}'
        return (
            f'class AfterHoursInfo {{ '
            f'Code={self.Code}, '
            f'Name={self.Name}, '
            f'TotalShare={totalShare}, '
            f'TotalTurnover={totalTurnover}, '
            f'OpenPrice={openPrice}, '
            f'HighestPrice={highestPrice}, '
            f'LowestPrice={lowestPrice}, '
            f'ClosePrice={closePrice} '
            f'}}'
        )
    # 檢查數值是否有效
    def checkNumber(self, value):
        if value == '--':
            return None
        else:
            return value
def main():
    resp = requests.get(
        f'https://www.twse.com.tw/exchangeReport/MI_INDEX?' +
        f'response=json&' +
        f'type=ALLBUT0999' +
        f'&date={datetime.date.today():%Y%m%d}'
    )
    if resp.status_code != 200:
        loguru.logger.error('RESP: status code is not 200')
    loguru.logger.success('RESP: success')
    afterHoursInfos = []
    body = resp.json()
    stat = body['stat']
    if stat != 'OK':
        loguru.logger.error(f'RESP: body.stat error is {stat}.')
        return
    records = body['data9']
    for record in records:
        code = record[0].strip()
        if re.match(r'^[1-9][0-9][0-9][0-9]$', code) is not None:
            name = record[1].strip()
            totalShare = record[2].replace(',', '').strip()
            totalTurnover = record[4].replace(',', '').strip()
            openPrice = record[5].replace(',', '').strip()
            highestPrice = record[6].replace(',', '').strip()
            lowestPrice = record[7].replace(',', '').strip()
            closePrice = record[8].replace(',', '').strip()
            afterHoursInfo = AfterHoursInfo(
                code=code,
                name=name,
                totalShare=totalShare,
                totalTurnover=totalTurnover,
                openPrice=openPrice,
                highestPrice=highestPrice,
                lowestPrice=lowestPrice,
                closePrice=closePrice
            )
            afterHoursInfos.append(afterHoursInfo)
    message = os.linesep.join([
        str(afterHoursInfo)
        for afterHoursInfo in afterHoursInfos
    ])
    loguru.logger.info('AFTERHOURSINFOS' + os.linesep + message)
if __name__ == '__main__':
    loguru.logger.add(
        f'{datetime.date.today():%Y%m%d}.log',
        rotation='1 day',
        retention='7 days',
        level='DEBUG'
    )
    main()
重新執行得到輸出結果。
https://docs.python.org/3/library/json.html
團隊系列文:
CSScoke - 金魚都能懂的這個網頁畫面怎麼切 - 金魚都能懂了你還怕學不會嗎
Clarence - LINE bot 好好玩 30 天玩轉 LINE API
Hina Hina - 陣列大亂鬥
King Tzeng - IoT沒那麼難!新手用JavaScript入門做自己的玩具
Vita Ora - 好 Js 不學嗎 !? JavaScript 入門中的入門。
TaTaMo - 用Python開發的網頁不能放到Github上?Lektor說可以!!
跟著用最後一個範例,我遇到兩個錯誤,一個是要import os,另一個是試在__init__()中,少了self.在checkNumber的部分。
init(): #裡面有用到checkNumber的部分
self.TotalShare = checkNumber(totalShare)
#要改成
self.TotalShare = self.checkNumber(totalShare)
謝謝樓主的教學與分享。
感謝找到程式碼錯誤之處,我馬上修正,謝謝您